package com.yunc.modules.deploy.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.text.StrSpliter;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunc.base.utils.ExecuteShellUtil;
import com.yunc.base.utils.R;
import com.yunc.modules.deploy.entity.Deploy;
import com.yunc.modules.deploy.entity.DeployApp;
import com.yunc.modules.deploy.entity.DeployServer;
import com.yunc.modules.deploy.mapper.DeployMapper;
import com.yunc.modules.deploy.service.IDeployAppService;
import com.yunc.modules.deploy.service.IDeployServerService;
import com.yunc.modules.deploy.service.IDeployService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 部署表 service实现
 *
 * @author concong tao
 * @since 2020-03-11
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DeployServiceImpl extends ServiceImpl<DeployMapper, Deploy> implements IDeployService {

    private final DeployMapper deployMapper;
    private final IDeployServerService deployServerService;
    private final IDeployAppService deployAppService;

    /**
     * 循环次数
     */
    private final Integer count = 30;

    @Override
    public R statusServer(String id) {
        Deploy deploy = this.getById(Long.valueOf(id));
        DeployApp app = deployAppService.getById(deploy.getAppId());
        List<String> serverIdList = StrSpliter.splitTrim(deploy.getServerIds(), ',', true);
        List<DeployServer> serverList = deployServerService.list(new QueryWrapper<DeployServer>().in("id", serverIdList));
        String info = "";
        for (DeployServer deployServer : serverList) {
            StringBuilder sb = new StringBuilder();
            ExecuteShellUtil executeShellUtil = new ExecuteShellUtil(deployServer.getIp(), deployServer.getAccount(), deployServer.getPassword(), deployServer.getPort());
            sb.append("服务器:").append(deployServer.getName()).append("<br>应用:").append(app.getName());
            boolean result = checkIsRunningStatus(app.getPort(), executeShellUtil);
            if (result) {
                sb.append("<br>正在运行");
            } else {
                sb.append("<br>已停止!");
            }
            log.info(sb.toString());
            info += sb.toString();
            executeShellUtil.close();
        }
        return R.ok(info);
    }

    @Override
    public R startServer(String id) {
        Deploy deploy = this.getById(Long.valueOf(id));
        DeployApp app = deployAppService.getById(deploy.getAppId());
        List<String> serverIdList = StrSpliter.splitTrim(deploy.getServerIds(), ',', true);
        List<DeployServer> serverList = deployServerService.list(new QueryWrapper<DeployServer>().in("id", serverIdList));
        for (DeployServer deployServer : serverList) {
            StringBuilder sb = new StringBuilder();
            ExecuteShellUtil executeShellUtil = new ExecuteShellUtil(deployServer.getIp(), deployServer.getAccount(), deployServer.getPassword(), deployServer.getPort());
            // 为了防止重复启动，这里先停止应用
            stopApp(app.getPort(), executeShellUtil);
            sb.append("服务器:").append(deployServer.getName()).append("<br>应用:").append(app.getName());
            Snowflake snowflake = IdUtil.createSnowflake(1, 1);
            String format = String.format(app.getStartScript(), snowflake.nextId());
            executeShellUtil.execute(format);
            sleep(3);
            log.info("应用启动中，请耐心等待启动结果，或者稍后手动查看运行状态");
            int i = 0;
            boolean result = false;
            // 由于启动应用需要时间，所以需要循环获取状态，如果超过30次，则认为是启动失败
            while (i++ < count) {
                result = checkIsRunningStatus(app.getPort(), executeShellUtil);
                if (result) {
                    break;
                }
                // 休眠6秒
                sleep(6);
            }
            log.info(sb.toString());
            executeShellUtil.close();
        }
        return R.ok();
    }

    @Override
    public R stopServer(String id) {
        Deploy deploy = this.getById(Long.valueOf(id));
        DeployApp app = deployAppService.getById(deploy.getAppId());
        List<String> serverIdList = StrSpliter.splitTrim(deploy.getServerIds(), ',', true);
        List<DeployServer> serverList = deployServerService.list(new QueryWrapper<DeployServer>().in("id", serverIdList));
        for (DeployServer deployServer : serverList) {
            StringBuilder sb = new StringBuilder();
            ExecuteShellUtil executeShellUtil = new ExecuteShellUtil(deployServer.getIp(), deployServer.getAccount(), deployServer.getPassword(), deployServer.getPort());
            sb.append("服务器:").append(deployServer.getName()).append("<br>应用:").append(app.getName());
            log.info("下发停止命令");
            // 停止应用
            stopApp(app.getPort(), executeShellUtil);
            sleep(1);
            boolean result = checkIsRunningStatus(app.getPort(), executeShellUtil);
            if (result) {
                sb.append("<br>关闭失败!");
            } else {
                sb.append("<br>关闭成功!");
            }
            log.info(sb.toString());
            executeShellUtil.close();
        }
        return R.ok();
    }

    /**
     * 停App
     *
     * @param port             端口
     * @param executeShellUtil /
     */
    private void stopApp(int port, ExecuteShellUtil executeShellUtil) {
        // 发送停止命令
        executeShellUtil.execute(String.format("lsof -i :%d|grep -v \"PID\"|awk '{print \"kill -9\",$2}'|sh", port));
    }

    /**
     * 指定端口程序是否在运行
     *
     * @param port             端口
     * @param executeShellUtil /
     * @return true 正在运行  false 已经停止
     */
    private boolean checkIsRunningStatus(int port, ExecuteShellUtil executeShellUtil) {
        String result = executeShellUtil.executeForResult(String.format("fuser -n tcp %d", port));
        return result.indexOf("/tcp:") > 0;
    }

    private void sleep(int second) {
        try {
            Thread.sleep(second * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}